#include <linux/types.h>//-
#include <linux/kernel.h>//-
#include <linux/delay.h>//-
#include <linux/ide.h>//-
#include <linux/init.h>//-
#include <linux/module.h>//-
#include <linux/errno.h>//-
#include <linux/gpio.h>//-
#include <linux/cdev.h>//-
#include <linux/device.h>//-
#include <linux/of.h>//-
#include <linux/of_address.h>//-
#include <linux/of_gpio.h>//-
#include <linux/semaphore.h>//-
#include <linux/timer.h>//-
#include <linux/of_irq.h>//-
#include <linux/irq.h>//-
#include <asm/mach/map.h>//-
#include <asm/uaccess.h>//-
#include <asm/io.h>//-
#include <linux/wait.h>//-
//-
#define IMX6UIRQ_CNT 1//-
#define IMX6UIRQ_NAME "imx6uirq"//-
#define KEY_NUM 1//-
#define KEY0VALUE 0x01//-
#define INVAKEY 0xFF//-
//-
struct irq_keydesc//-
{//-
    int gpio;//-
    int irqnum;//-
    unsigned char value;//-
    char name[10];//-
    irqreturn_t (*handler)(int, void *);//-
    struct tasklet_struct tasklet;//-
};//-
//-
struct imx6uirq_dev//-
{//-
    dev_t devid;//-
    int major;//-
    int minor;//-
    struct class *class;//-
    struct device *device;//-
    struct cdev cdev;//-
    struct device_node *node;//-
    struct irq_keydesc irqkey[KEY_NUM];//-
    struct timer_list timer;//-

//-
    atomic_t keyvalue;//-
    atomic_t releasekey;//-
    wait_queue_head_t r_wait;       //等待队列头//-
//-
};//-
static struct imx6uirq_dev imx6uirqdev;//-
//-
static int imx6uirq_open(struct inode *inode, struct file *file)//-
{//-
    file->private_data = &imx6uirqdev;//-
    return 0;//-
}//-
static ssize_t imx6uirq_read(struct file *file, char __user *buf, size_t cnt, loff_t *offt)//-
{//-
    struct imx6uirq_dev *dev = file->private_data;//-
    int ret = 0;
    unsigned char keyvalue = 0;
    unsigned char keyrelease = 0;//-

    //wait_event_interruptible(dev->r_wait, atomic_read(&dev->releasekey));

    DECLARE_WAITQUEUE(wait, current);
    add_wait_queue(&dev->r_wait, &wait);
    __set_current_state(TASK_INTERRUPTIBLE);
    schedule();

    if(signal_pending(current)){
        ret = -ERESTARTSYS;
        goto data_error;
    }

    keyvalue = atomic_read(&dev->keyvalue);
    keyrelease = atomic_read(&dev->releasekey);
    if (keyrelease)                 
    {
        printk("keyrelease = %d\n", keyrelease);
        if (keyvalue & 0x80)
        {
            keyvalue &= ~0x80;
            printk("keyvalue is %#x\r\n",keyvalue);

            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        }else{
            goto data_error;
        }
        atomic_set(&dev->releasekey, 0);
        /* code */
    }
    else
    {
        goto data_error;
    }

data_error:
    __set_current_state(TASK_RUNNING);
    remove_wait_queue(&dev->r_wait, &wait);

    return ret;//-
}//-
static int imx6uirq_release(struct inode *inode, struct file *file)//-
{//-
//-
    return 0;//-
}//-
static const struct file_operations imx6uirq_fops = {//-
    .owner = THIS_MODULE,//-
    .open = imx6uirq_open,//-
    .read = imx6uirq_read,//-
    .release = imx6uirq_release,//-
};//-
static irqreturn_t key0_handler(int irq,void *dev_id)//-
{//-
    struct imx6uirq_dev *dev = dev_id;//-
    tasklet_schedule(&dev->irqkey[0].tasklet);//-
    return IRQ_HANDLED;//-
}//-
static void key_tasklet(unsigned long data)//-
{//-
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)data;//-
    printk("key_tasklet is runing\r\n");//-
    dev->timer.data = data;//-
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));//-
}//-
static void timer_func(unsigned long data)//-
{//-
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)data;//-
    int value = 0;//-
    printk("timer_func is runing\r\n");//-
    value = gpio_get_value(dev->irqkey[0].gpio);//-
    if (value == 0){//-
        atomic_set(&dev->keyvalue,dev->irqkey[0].value);//-
    }//-
    else if (value == 1)//-
    {//-
        atomic_set(&dev->keyvalue,(dev->irqkey[0].value) | 0x80);//-
        atomic_set(&dev->releasekey,1);//-
    }//-
    if (atomic_read(&dev->releasekey))      //-
    {//-
        wake_up(&dev->r_wait);//-
    }//-
}//-
static int keyio_init(struct imx6uirq_dev *dev)//-
{//-
    int i = 0;//-
    int ret = 0;//-
    // 1、按键初始化//-
    dev->node = of_find_node_by_path("/key");//-
    if (dev->node == NULL)//-
    {//-
        ret = -EINVAL;//-
        goto fail_node;//-
    }//-
    for (i = 0; i < KEY_NUM; i++)//-
    {//-
        dev->irqkey[i].gpio = of_get_named_gpio(dev->node, "key-gpios", i);//-
    }//-
    for (i = 0; i < KEY_NUM; i++)//-
    {//-
        memset(&dev->irqkey[i].name, 0, sizeof(dev->irqkey[i].name));//-
        sprintf(dev->irqkey[i].name, "key%d", i);//-
        gpio_request(dev->irqkey[i].gpio, dev->irqkey[i].name);//-
        ret = gpio_direction_input(dev->irqkey[i].gpio);//-
//-
        dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio); /*获取中断号*///-
    }//-
    /* 2、按键中断初始化 *///-
    dev->irqkey[0].handler = key0_handler;//-
    dev->irqkey[0].value = KEY0VALUE;//-
    for (i = 0; i < KEY_NUM; i++)//-
    {//-
        ret = request_irq(dev->irqkey->irqnum, dev->irqkey[i].handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,//-
                          dev->irqkey[i].name, &imx6uirqdev);//-
        if (ret)//-
        {//-
            printk("irq %d requeset fail\r\n", dev->irqkey[i].irqnum);//-
            goto fail_irqrequest;//-
        }//-
        tasklet_init(&dev->irqkey[i].tasklet,key_tasklet,(unsigned long)dev);//-
//-
    }//-
    init_timer(&dev->timer);//-
    dev->timer.function = timer_func;//-
//-
    return 0;//-
fail_irqrequest://-
    for (i = 0; i < KEY_NUM; i++)//-
    {//-
        gpio_free(dev->irqkey[i].gpio);//-
    }//-
fail_node://-
    return ret;//-
}//-
//-
static int __init imx6uirq_init(void)//-
{//-
    int ret = 0;//-
    printk("imx6uirq_init\r\n");//-
    imx6uirqdev.major = 0;//-
    ret = alloc_chrdev_region(&imx6uirqdev.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);//-
    if (ret < 0)//-
    {//-
        printk("alloc_chrdev_region failed\r\n");//-
        goto fail_devid;//-
    }//-
    printk("major=%d,minor=%d\r\n", MAJOR(imx6uirqdev.devid), MINOR(imx6uirqdev.devid));//-
    ;//-
    imx6uirqdev.cdev.owner = THIS_MODULE;//-
    cdev_init(&imx6uirqdev.cdev, &imx6uirq_fops);//-
    ret = cdev_add(&imx6uirqdev.cdev, imx6uirqdev.devid, IMX6UIRQ_CNT);//-
    if (ret < 0)//-
    {//-
        printk("cdev_add failed\r\n");//-
        goto fail_cdev;//-
    }//-
//-
    imx6uirqdev.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);//-
    if (IS_ERR(imx6uirqdev.class))//-
    {//-
        ret = PTR_ERR(imx6uirqdev.class);//-
        goto fail_class;//-
    }//-
    imx6uirqdev.device = device_create(imx6uirqdev.class, NULL, imx6uirqdev.devid, NULL, IMX6UIRQ_NAME);//-
    if (IS_ERR(imx6uirqdev.device))//-
    {//-
        ret = PTR_ERR(imx6uirqdev.device);//-
        goto fail_device;//-
    }//-
    /* 初始化keyIO *///-
    ret = keyio_init(&imx6uirqdev);//-
    if (ret < 0)//-
    {//-
        goto fail_keyIO_init;//-
    }//
    /* 初始化等待队列头 */
    init_waitqueue_head(&imx6uirqdev.r_wait);
    return 0;  //-
fail_keyIO_init://-
    device_destroy(imx6uirqdev.class, imx6uirqdev.devid);//-
fail_device://-
    class_destroy(imx6uirqdev.class);//-
fail_class://-
    cdev_del(&imx6uirqdev.cdev);//-
fail_cdev://-
    unregister_chrdev_region(imx6uirqdev.devid, IMX6UIRQ_CNT);//-
fail_devid://-
//-
    return ret;//-
}//-
static void __exit imx6uirq_exit(void)//-
{//-
    int i = 0;//-
    printk("imx6uirq_exit\r\n");//-
    /*1、释放中断*///-
    for (i = 0; i < KEY_NUM; i++)//-
    {//-
        free_irq(imx6uirqdev.irqkey[i].irqnum, &imx6uirqdev);//-
    }//-
    for (i = 0; i < KEY_NUM; i++)//-
    {//-
        gpio_free(imx6uirqdev.irqkey[i].gpio);//-
    }//-
    /*2\删除定时器*///-
    del_timer_sync(&imx6uirqdev.timer);//-
    unregister_chrdev_region(imx6uirqdev.devid, IMX6UIRQ_CNT);//-
    cdev_del(&imx6uirqdev.cdev);//-
    device_destroy(imx6uirqdev.class, imx6uirqdev.devid);//-
    class_destroy(imx6uirqdev.class);//-
}//-
//-
module_init(imx6uirq_init);//-
module_exit(imx6uirq_exit);//-
MODULE_LICENSE("GPL");//-
MODULE_AUTHOR("WXY");//-
MODULE_DESCRIPTION("imx6uirq");//
